import Ele from './ele/Ele'
import EleTreeBuilder from './builder/EleTreeBuilder'
import { is_action, is_data, is_number, run } from './util'
import OkayProxy from './proxy/OkayProxy'
import Component from './Component'
import ActionRouter from './router/ActionRouter'
import Action from './Action'
export default class Okay {
    constructor (config) {
        if (config.on_start) {
           this.on_start = config.on_start
            config.on_start.call(this)
        }
        this.eventMap = new Map()
        this.actionEvent = new Map()
        this.filters = config.filters || {}
        this.comps = new Map()
        this.aop = config.aop || {}
        if (config.components) {
            Object.keys(config.components).forEach((key) => {
                this.comps.set(key, config.components[key])
            })
        }

        if (config.parent != undefined) {
            this.parent = config.parent
            if(config.parent_insert_type == 'prepend') {
                this.parent.okays.unshift(this)
            }else {
                this.parent.okays.push(this)
            }

        }else {
            this.parent = null
        }
        this.action_classes = config.action_classes || {}
        this.initActions()

        this.parent_field = config.parent_field || null

        config.data?null:config.data = {}
        // 代理数据
        let proxy = config.data.proxy || new OkayProxy(config.data)
        this.data = config.data || {}
        proxy.okays.add(this)

        //路由绑定
        this.routes = config.routes || []
        this.route = {}

        // 构建VNode树
        this.ele = new Ele(this, ((config.ele instanceof Element) || (config.ele instanceof DocumentFragment)) ? config.ele : document.getElementById(config.ele))
        this.builder = config.builder || new EleTreeBuilder()
        this.builder.build(this.ele, this)
        // 表单绑定
        this.ele.ele.querySelectorAll('[model]').forEach(ele => {
            if (ele.oninput) {
                return;
            }
            if ((ele.getAttribute('type') == 'radio' || ele.getAttribute('type') == 'checkbox') && !ele.onchange) {
                ele.onchange = e=> {
                    e.stopPropagation()
                    let target = e.target
                    let model = ''
                    if (!(model = target.getAttribute('model'))) {
                        return true
                    }
                    let fn, val = target.value
                    let target_model = this.find_var(model)
                    if (target_model) {
                        fn = new Function('val', target_model + ' = val')
                    }else {
                        throw this.ele.outerHTML + '：未找到绑定的对象-->' + model
                    }
                    let type = ele.getAttribute('type')
                    if (type == 'checkbox') {
                        let name = target.getAttribute('name')
                        if (/^\w+\[\]$/.test(name)) {
                            if (target.checked == true) {
                                fn = new Function('val', target_model + '.push(val);')
                            } else {
                                fn = new Function('val', 'let index = '+ target_model + ".indexOf(val);" + target_model + '.splice(index,1);')
                            }
                        } else {
                            if (!target.checked) {
                                val = '';
                            }
                        }
                    }
                    fn.call(this, val)
                }
            }else {
                ele.oninput = e => {
                    e.stopPropagation();
                    let target = e.target
                    let model = ''
                    if (!(model = target.getAttribute('model'))) {
                        return true
                    }
                    let fn, val = target.value
                    let target_model = this.find_var(model)
                    if (target_model) {
                        fn = new Function('val', target_model + ' = val')
                    } else {
                        throw this.ele.outerHTML + '：未找到绑定的对象-->' + model
                    }
                    fn.call(this, val)
                }
            }
        })



        this.listen_action()
        this.listen_route()
        if (config.on_create) {
            this.on_create = config.on_create
            config.on_create.call(this)
        }
        this.ele.draw()
        if (config.on_resume) {
            this.on_resume = config.on_resume
            config.on_resume.call(this)
        }
        if (config.on_pause) {
            this.on_pause = config.on_pause
        }
        if (config.on_stop) {
            this.on_stop = config.on_stop
        }
        if (config.on_destroy) {
            this.on_destroy = config.on_destroy
        }
        if (config.on_restart) {
            this.on_restart = config.on_restart
        }
    }
    listen_route () {
        this.ele.ele.querySelectorAll('[link-to]').forEach(ele => {
            if (ele.onclick ) {
                return;
            }
            ele.onclick = e => {
                let target = e.target
                if (target.getAttribute('link-to')) {
                    let linkTo = target.getAttribute('link-to')
                    let route = target.getAttribute('target')
                    route = route?route:'default'
                    e.preventDefault()
                    this.go(linkTo, false, route)
                }
            }
        })
    }
    find_route (target) {
        if (!this.route[target]) {
            if(!this.parent) {
                throw '未找到路由视图'
            }else {
                return this.parent.subject.find_route(target)
            }
        }
        return this.route[target]
    }
    go (linkto, is_new = false, target = 'default') {
        let route = this.find_route(target)
        if(is_number(linkto)) {
            route.go(parseInt(linkto.replace(/\s/g, '')))
        }else {
            route.route(linkto, is_new)
        }
    }
    find_var (ev, parent = '') {
        if (!is_data(ev, this) && !is_action(ev, this)) {
            if (!this.parent) {
                return false
            }else {
                let current_object = ev.split('.')[0]
                if (this.parent_field
                    && this.parent_field.key
                    && current_object == this.parent_field.key) {
                    let index = this.parent.okays.indexOf(this)
                    ev = this.parent_field.source + '[' + index + ']' + ev.replace(current_object, '')
                    if (is_data(ev, this)) {
                        return 'this.'+ parent + 'data.' + ev
                    } else if (is_action(ev, this)) {
                        return 'this.' + parent + 'actions.' + ev
                    } else {
                        return this.parent.subject.find_var(ev, 'parent.subject.' + parent)
                    }
                }
            }
        }else {
            if(is_data(ev, this)) {
                return 'this.' + parent + 'data.' + ev
            }else {
                return 'this.' + parent + 'actions.' + ev
            }
        }
    }

    /**
     * Action事件监听
     * action:事件名|action名=>ret0:action名0,ret1:action名1|action名=>ret0:action名0,ret1:action名1
     */
    listen_action () {
        ['abort',
        'blur',
        'change',
        'click',
        'dbclick',
        'error',
        'focus',
        'keydown',
        'keypress',
        'keyup',
        'load',
        'mousedown',
        'mousemove',
        'mouseout',
        'mouseover',
        'mouseup',
        'reset',
        'resize',
        'select',
        'submit',
        'unload',
        'touchstart',
        'touchmove',
        'touchend',
        'touchcancel'].forEach((ev) => {
            this.ele.ele.querySelectorAll('[action-' + ev + ']').forEach((ele)=>{
                if (ele['on' + ev] != null || ele['on' + ev] !=undefined) {
                    return
                }
                ele['on' + ev]  = (e) => {
                    let action = e.target.getAttribute('action-' + ev )
                    let props = e.target.getAttribute('action-'+ ev +'-props')
                    let aopBefore = e.target.getAttribute('aop-' + ev + '-before')
                    let aopAfter = e.target.getAttribute('aop-' + ev + '-after')
                    let router = new ActionRouter(action)
                    let targetAction = router.route()
                    let pipe = new Map()
                    let before = new Map()
                    let after = new Map()
                    if(props) {
                        let ps = props.split(',')
                        ps.forEach(key => {
                            let tmp = key.split(':')
                            pipe.set(tmp[0], tmp[1])
                        })
                    }
                    if (aopBefore) {
                        let bb = aopBefore.split(',')
                        bb.forEach(key => {
                            let tmp = key.split(':')
                            before.set(tmp[0],tmp[1])
                        })
                    }
                    if(aopAfter) {
                        let aa = aopAfter.split(',')
                        aa.forEach(key => {
                            let tmp = key.split(':')
                            after.set(tmp[0], tmp[1])
                        })
                    }
                    let that = this
                    run(function *() {
                        while (targetAction != undefined) {
                            if (is_action(targetAction, that)) {
                                if(before.has('_start_')) {
                                    let bf = before.get('_start_')
                                    if(that.aop[bf]) {
                                        that.aop[bf].call(that, pipe)
                                    }
                                }
                                if(before.has(targetAction)) {
                                    let beforeFn = before.get(targetAction)
                                    if(that.aop[beforeFn]) {
                                        that.aop[beforeFn].call(that,pipe)
                                    }
                                }
                                let result = yield ((new Function('okay','event','pipe','return this.actions.' + targetAction + '(okay, event, pipe)')).call(that, that, e, pipe))
                                if(after.has(targetAction)) {
                                    let afterFn = after.get(targetAction)
                                    if(that.aop[afterFn]) {
                                        that.aop[afterFn].call(that,pipe)
                                    }
                                }
                                if(result == undefined || result == Action.EOA){
                                    targetAction = undefined
                                    if(after.has('_end_')) {
                                        let af = after.get('_end_')
                                        if(that.aop[af]) {
                                            that.aop[af].call(that, pipe)
                                        }
                                    }
                                    break;
                                }else{
                                    targetAction = router.route(result)
                                }
                            }else {
                                throw '未定义Action:' + targetAction
                            }
                        }
                    })

                }
            })
        })
    }

    /**
     *初始化Action
    */
    initActions () {
        this.actions = {}
        Object.keys(this.action_classes).forEach((key)=>{
            this.actions[key] = this.action_classes[key].i()
            this.actions[key].addOkay(this)
        })

    }

    /**
     *过滤器函数处理
     */
    filter (name, ...args) {
        if (this.filters[name] != undefined
            && this.filters[name] instanceof Function) {
            return this.filters[name](...args)
        }else {
            if (this.parent != undefined) {
                this.parent.subject.filter(name, ...args)
            }else {
                throw '未定义过滤器：' + name
            }
        }
    }

    /**
     * 订阅数据变化
     * @param ev
     * @param ele
     */
    subscribe(ev, ele, parent = '') {
        if (!is_data(ev, this) && !is_action(ev, this)) {
            if (!this.parent) {
                return false
            }else {
                let current_object = ev.split('.')[0]
                if (this.parent_field
                    && this.parent_field.key
                    && current_object == this.parent_field.key) {
                    let index = this.parent.okays.indexOf(this)
                    ev = this.parent_field.source + '[' + index + ']' + ev.replace(current_object, '')
                    if (is_data(ev, this)) {
                        let key = ev.replace(/\[/g, '.').replace(/\]/g, '')
                        if (this.eventMap.has(key)) {
                            this.eventMap.get(key).add(ele)
                        } else {
                            let set = new Set([ele])
                            this.eventMap.set(key, set)
                        }
                        return 'this.'+ parent +'data.'  + ev
                    }else if (is_action(ev, this)) {
                        // TODO: Action监听
                        let key = ev.replace(/\[/g, '.').replace(/\]/g, '')
                        let action_name = key.substr(0,key.indexOf('.'))
                        let event_key = key.substr(key.indexOf('.') + 1)
                        let action = (new Function('return this.actions.' + action_name)).call(this)
                        if (this.actionEvent.has(action)) {
                            let eve = this.actionEvent.get(action)
                            if(eve.has(event_key)) {
                                eve.get(event_key).add(ele)
                            }else {
                                let set = new Set([ele])
                                eve.set(event_key, set)
                            }
                        }else {
                            let eve_map = new Map()
                            let set = new Set([ele])
                            eve_map.set(event_key, set)
                            this.actionEvent.set(action, eve_map)

                        }
                        return 'this.' + parent + 'actions.'  + ev
                    }else {
                        return this.parent.subject.subscribe(ev, ele, 'parent.subject.' + parent)
                    }
                }else {
                    return this.parent.subject.subscribe(ev, ele, 'parent.subject.' + parent)
                }
            }
        }else {
            if (is_data(ev, this)) {
                let key = ev.replace(/\[/g, '.').replace(/\]/g, '')
                if (this.eventMap.has(key)) {
                    this.eventMap.get(key).add(ele)
                } else {
                    let set = new Set([ele])
                    this.eventMap.set(key, set)
                }
                return 'this.' + parent + 'data.' + ev
            } else {
                //TODO: Action监听
                let key = ev.replace(/\[/g, '.').replace(/\]/g, '')
                let action_name = key.substr(0,key.indexOf('.'))
                let event_key = key.substr(key.indexOf('.') + 1)
                let action = (new Function('return this.actions.' + action_name)).call(this)
                if (this.actionEvent.has(action)) {
                    let eve = this.actionEvent.get(action)
                    if(eve.has(event_key)) {
                        eve.get(event_key).add(ele)
                    }else {
                        let set = new Set([ele])
                        eve.set(event_key, set)
                    }
                }else {
                    let eve_map = new Map()
                    let set = new Set([ele])
                    eve_map.set(event_key, set)
                    this.actionEvent.set(action, eve_map)

                }
                return 'this.' + parent + 'actions.' + ev
            }
        }

    }

    /**
     * 数据变化事件触发
     * @param ev
     */
    publish(ev, data = {}) {
        if (this.eventMap.has(ev)) {
            this.eventMap.get(ev).forEach(function(value, key, own){
                value.update(ev, data);
            })
        }
    }
    actions_publish(action, ev, data = {}) {
        if (this.actionEvent.has(action)) {
            let event_map = this.actionEvent.get(action)
            if (event_map.has(ev)) {
                event_map.get(ev).forEach((ele) => {
                    ele.update(ev, data)
                })
            }
        }
    }
    static use(name) {
        switch (name) {
            case 'comps':
                window.comps = {}
                Object.assign(comps,{
                    load (name,config){
                        if(!this.u){
                            this.u = new Set()
                        }
                        let doms = document.querySelectorAll(name)
                        for(let dom of doms){
                            let comp = new Component(dom,cfg)
                        }
                    }
                })
                break
            case 'router':
                break
            default:
                if(!Okay.plugins.has(name)) {
                    console.error("未定义：" + name + '插件')
                }else{
                    let plugin = Okay.plugins.get(name)
                    window[name] = {}
                    plugin.init()
                }
                break
        }
    }
    static plugin(name,plugin) {
        return Okay.plugins.set(name,plugin)
    }
    static component (name, config) {
        Okay.components.set(name, config)
    }
}
Okay.components = new Map()
Okay.plugins = new Map()

